// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/tools/flip_server/streamer_interface.h"

#include <string>

#include "net/tools/balsa/balsa_frame.h"
#include "net/tools/flip_server/constants.h"
#include "net/tools/flip_server/flip_config.h"
#include "net/tools/flip_server/sm_connection.h"

namespace net {

std::string StreamerSM::forward_ip_header_;

StreamerSM::StreamerSM(SMConnection* connection,
    SMInterface* sm_other_interface,
    EpollServer* epoll_server,
    FlipAcceptor* acceptor)
    : connection_(connection)
    , sm_other_interface_(sm_other_interface)
    , epoll_server_(epoll_server)
    , acceptor_(acceptor)
    , is_request_(false)
    , http_framer_(new BalsaFrame)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Creating StreamerSM object";
    http_framer_->set_balsa_visitor(this);
    http_framer_->set_balsa_headers(&headers_);
    http_framer_->set_is_request(false);
}

StreamerSM::~StreamerSM()
{
    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Destroying StreamerSM object";
    Reset();
    delete http_framer_;
}

void StreamerSM::set_is_request()
{
    is_request_ = true;
    http_framer_->set_is_request(true);
}

void StreamerSM::InitSMInterface(SMInterface* sm_other_interface,
    int32_t server_idx)
{
    sm_other_interface_ = sm_other_interface;
}

void StreamerSM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
    SMInterface* sm_interface,
    EpollServer* epoll_server,
    int fd,
    std::string server_ip,
    std::string server_port,
    std::string remote_ip,
    bool use_ssl)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StreamerSM: Initializing server "
            << "connection.";
    connection_->InitSMConnection(connection_pool,
        sm_interface,
        epoll_server,
        fd,
        server_ip,
        server_port,
        remote_ip,
        use_ssl);
}

size_t StreamerSM::ProcessReadInput(const char* data, size_t len)
{
    // For now we only want to parse http requests. Just stream responses
    if (is_request_) {
        return http_framer_->ProcessInput(data, len);
    } else {
        return sm_other_interface_->ProcessWriteInput(data, len);
    }
}

size_t StreamerSM::ProcessWriteInput(const char* data, size_t len)
{
    char* dataPtr = new char[len];
    memcpy(dataPtr, data, len);
    DataFrame* df = new DataFrame;
    df->data = (const char*)dataPtr;
    df->size = len;
    df->delete_when_done = true;
    connection_->EnqueueDataFrame(df);
    return len;
}

bool StreamerSM::Error() const { return false; }

const char* StreamerSM::ErrorAsString() const { return "(none)"; }

bool StreamerSM::MessageFullyRead() const
{
    if (is_request_) {
        return http_framer_->MessageFullyRead();
    } else {
        return false;
    }
}

void StreamerSM::Reset()
{
    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "StreamerSM: Reset";
    connection_->Cleanup("Server Reset");
    http_framer_->Reset();
}

void StreamerSM::ResetForNewConnection()
{
    http_framer_->Reset();
    sm_other_interface_->Reset();
}

void StreamerSM::Cleanup()
{
    if (is_request_)
        http_framer_->Reset();
}

int StreamerSM::PostAcceptHook()
{
    if (!sm_other_interface_) {
        SMConnection* server_connection = SMConnection::NewSMConnection(
            epoll_server_, NULL, NULL, acceptor_, "server_conn: ");
        if (server_connection == NULL) {
            LOG(ERROR) << "StreamerSM: Could not create server conenction.";
            return 0;
        }
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StreamerSM: Creating new server "
                << "connection.";
        sm_other_interface_ = new StreamerSM(server_connection, this, epoll_server_, acceptor_);
        sm_other_interface_->InitSMInterface(this, 0);
    }
    // The Streamer interface is used to stream HTTPS connections, so we
    // will always use the https_server_ip/port here.
    sm_other_interface_->InitSMConnection(NULL,
        sm_other_interface_,
        epoll_server_,
        -1,
        acceptor_->https_server_ip_,
        acceptor_->https_server_port_,
        std::string(),
        false);

    return 1;
}

size_t StreamerSM::SendSynStream(uint32_t stream_id,
    const BalsaHeaders& headers)
{
    return 0;
}

size_t StreamerSM::SendSynReply(uint32_t stream_id,
    const BalsaHeaders& headers)
{
    return 0;
}

void StreamerSM::ProcessBodyInput(const char* input, size_t size)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT
            << "StreamerHttpSM: Process Body Input Data: "
            << "size " << size;
    sm_other_interface_->ProcessWriteInput(input, size);
}

void StreamerSM::MessageDone()
{
    if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StreamerHttpSM: MessageDone.";
        // TODO(kelindsay): anything need to be done ehre?
    } else {
        VLOG(2) << ACCEPTOR_CLIENT_IDENT << "StraemerHttpSM: MessageDone.";
    }
}

void StreamerSM::ProcessHeaders(const BalsaHeaders& headers)
{
    VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpStreamerSM: Process Headers";
    BalsaHeaders mod_headers;
    mod_headers.CopyFrom(headers);
    if (forward_ip_header_.length()) {
        LOG(INFO) << "Adding forward header: " << forward_ip_header_;
        mod_headers.ReplaceOrAppendHeader(forward_ip_header_,
            connection_->client_ip());
    } else {
        LOG(INFO) << "NOT adding forward header.";
    }
    SimpleBuffer sb;
    char* buffer;
    int size;
    mod_headers.WriteHeaderAndEndingToBuffer(&sb);
    sb.GetReadablePtr(&buffer, &size);
    sm_other_interface_->ProcessWriteInput(buffer, size);
}

void StreamerSM::HandleHeaderError(BalsaFrame* framer) { HandleError(); }

void StreamerSM::HandleChunkingError(BalsaFrame* framer) { HandleError(); }

void StreamerSM::HandleBodyError(BalsaFrame* framer) { HandleError(); }

void StreamerSM::HandleError()
{
    VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Error detected";
}

} // namespace net
